package com.unisound.vpr.demo;

import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.unisound.vpr.demo.VprConstants.CONFIRM_FEATURE_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.CREATE_FEATURE_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.CREATE_GROUP_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.DEL_FEATURE_BY_ID_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.DEL_GROUP_BY_ID_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.FIND_FEATURE_LIST_BY_GROUP_ID_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.IDENTIFY_FEATURE_BY_GROUP_ID_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.IDENTIFY_FEATURE_BY_IDS_ENDPOINT;
import static com.unisound.vpr.demo.VprConstants.UPDATE_FEATURE_BY_ID_ENDPOINT;

/**
 * 声纹识别Demo
 *
 * @author YZS
 */
@Slf4j
public class VprDemo {
    /**
     * 服务地址
     */
    private static final String HOST = "https://ai-vpr.hivoice.cn";
    /**
     * 控制台首页查看你的AppKey
     */
    public static final String APP_KEY = "********************************";
    /**
     * 控制台首页查看你的secret
     */
    public static final String SECRET = "********************************";
    /**
     * 声纹组ID
     */
    public static final String GROUP_ID = "********************************";
    /**
     * 声纹ID 创建声纹成功可记录此ID  [可自定义，建议UUID,不能超过128位]
     */
    public static final String FEATURE_ID = "********************************";
    /**
     * 其他声纹组ID
     */
    public static final String OTHER_GROUP_ID = "********************************";
    /**
     * 其他声纹组 声纹ID
     */
    public static final String OTHER_FEATURE_ID = "********************************";
    /**
     * 创建声纹信息描述 自行定义
     */
    public static final String FEATURE_INFO = "**********我的声纹信息描述************";
    /**
     * 创建声纹组信息描述 自行定义
     */
    public static final String GROUP_INFO = "**********我的声纹组信息描述************";
    /**
     * 需要辨认的音频文件名, 将需要辨认的声纹音频放到resource目录下, 文件名自行定义|修改
     */
    public static final String CONFIRM_FEATURE_FILE_NAME = "confirmFeature.mp3";
    /**
     * 需要创建的音频文件名, 将需要辨认的声纹音频放到resource目录下, 文件名自行定义|修改
     */
    public static final String CREATE_FEATURE_FILE_NAME = "createFeature.mp3";

    public static void main(String[] args) {
        // 创建声纹组
        createGroup();
        // 创建声纹
        createFeature();
        // 声纹确认 1:1
        confirmFeature();
        // 声纹确认 1:N 组内所有声纹
        identifyFeatureByGroupId();
        // 声纹确认 1:N 不同组的多个声纹
        identifyFeatureByIds();
        // 查询组内声纹列表
        findFeatureListByGroupId();
        // 修改声纹
        updateFeatureById();
        // 删除声纹
        delFeatureById();
        // 删除声纹组
        delGroupById();
    }

    /**
     * 查询组内声纹列表
     */
    private static void findFeatureListByGroupId() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        Dict findFeatureListByGroupIdParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID);
        String result = HttpUtil.post(HOST + FIND_FEATURE_LIST_BY_GROUP_ID_ENDPOINT, JSONUtil.toJsonStr(findFeatureListByGroupIdParam));
        log.info("删除声纹组, 返回结果: {}", result);
    }

    /**
     * 删除声纹组
     */
    private static void delGroupById() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        Dict delGroupByIdParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID);
        String result = HttpUtil.post(HOST + DEL_GROUP_BY_ID_ENDPOINT, JSONUtil.toJsonStr(delGroupByIdParam));
        log.info("删除声纹组, 返回结果: {}", result);
    }

    /**
     * 删除声纹
     */
    private static void delFeatureById() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        Dict delFeatureByIdParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID)
                // 声纹id[可自定义，建议UUID,不能超过128位]
                .set("featureId", FEATURE_ID);
        String result = HttpUtil.post(HOST + DEL_FEATURE_BY_ID_ENDPOINT, JSONUtil.toJsonStr(delFeatureByIdParam));
        log.info("删除声纹, 返回结果: {}", result);
    }

    /**
     * 修改声纹
     */
    private static void updateFeatureById() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        byte[] bytes = IoUtil.readBytes(ClassLoader.getSystemResourceAsStream(CONFIRM_FEATURE_FILE_NAME));
        // 声纹确认Base64字符串
        String audioData = Base64Encoder.encode(bytes);
        Dict updateFeatureByIdParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID)
                // 声纹id[可自定义，建议UUID,不能超过128位]
                .set("featureId", FEATURE_ID)
                // 声纹描述
                .set("featureInfo", "我的声纹")
                // 音频数据base64编码（编码后最小长度:1B 最大长度:4M）
                .set("audioData", audioData)
                // 采样率
                .set("audioSampleRate", 16000)
                // 音频格式  pcm、mp3、opus、opus-yzs
                .set("audioFormat", "mp3");
        String result = HttpUtil.post(HOST + UPDATE_FEATURE_BY_ID_ENDPOINT, JSONUtil.toJsonStr(updateFeatureByIdParam));
        log.info("修改声纹, 返回结果: {}", result);
    }

    /**
     * 声纹确认 1:N 不同组的多个声纹
     */
    private static void identifyFeatureByIds() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        byte[] bytes = IoUtil.readBytes(ClassLoader.getSystemResourceAsStream(CONFIRM_FEATURE_FILE_NAME));
        // 声纹确认Base64字符串
        String audioData = Base64Encoder.encode(bytes);
        Dict featureDict = Dict.create().set("groupId", GROUP_ID).set("featureId", FEATURE_ID);
        // 其他组的  需确定本组中 需要对比的声纹ID 可通过查询组内声纹列表接口确定声纹ID
        Dict otherFeatureDict = Dict.create().set("groupId", OTHER_GROUP_ID).set("featureId", OTHER_FEATURE_ID);
        // 不同组内的多个声纹id
        List<Object> featureList = new ArrayList<>(Arrays.asList(featureDict, otherFeatureDict));
        Dict identifyFeatureByIdsParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("featureList", featureList)
                // 返回前几条，最小1 最大10
                .set("topN", "1").set("audioData", audioData).set("audioSampleRate", 16000).set("audioFormat", "mp3");
        String result = HttpUtil.post(HOST + IDENTIFY_FEATURE_BY_IDS_ENDPOINT, JSONUtil.toJsonStr(identifyFeatureByIdsParam));
        log.info("声纹确认1:N,返回结果: {}", result);
    }

    /**
     * 声纹辨认【1:N】
     */
    private static void identifyFeatureByGroupId() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        byte[] bytes = IoUtil.readBytes(ClassLoader.getSystemResourceAsStream(CONFIRM_FEATURE_FILE_NAME));
        // 声纹确认Base64字符串
        String audioData = Base64Encoder.encode(bytes);
        Dict identifyFeatureParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID)
                // 返回前几条，最小1 最大10
                .set("topN", "1").set("audioData", audioData).set("audioSampleRate", 16000).set("audioFormat", "mp3");
        String result = HttpUtil.post(HOST + IDENTIFY_FEATURE_BY_GROUP_ID_ENDPOINT, JSONUtil.toJsonStr(identifyFeatureParam));
        log.info("声纹确认1:N, 返回结果: {}", result);
    }

    /**
     * 声纹确认【1:1】
     */
    private static void confirmFeature() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        byte[] bytes = IoUtil.readBytes(ClassLoader.getSystemResourceAsStream(CONFIRM_FEATURE_FILE_NAME));
        // 声纹确认Base64字符串
        String audioData = Base64Encoder.encode(bytes);
        Dict confirmFeatureParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce).set("sign", sign)
                .set("groupId", GROUP_ID).set("featureId", FEATURE_ID).set("audioData", audioData)
                .set("audioSampleRate", 16000).set("audioFormat", "mp3");
        String result = HttpUtil.post(HOST + CONFIRM_FEATURE_ENDPOINT, JSONUtil.toJsonStr(confirmFeatureParam));
        log.info("声纹确认1:1, 返回结果: {}", result);
    }

    /**
     * 创建声纹
     */
    private static void createFeature() {
        byte[] bytes = IoUtil.readBytes(ClassLoader.getSystemResourceAsStream(CREATE_FEATURE_FILE_NAME));
        // 声纹Base64字符串
        String audioData = Base64Encoder.encode(bytes);
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        // 组装 创建声纹 参数
        Dict createFeatureParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID)
                // 声纹id[可自定义，建议UUID,不能超过128位]
                .set("featureId", FEATURE_ID)
                // 声纹描述
                .set("featureInfo", FEATURE_INFO)
                // 音频数据base64编码（编码后最小长度:1B 最大长度:4M）
                .set("audioData", audioData)
                // 采样率
                .set("audioSampleRate", 16000)
                // 音频格式  pcm、mp3、opus、opus-yzs
                .set("audioFormat", "mp3");
        String result = HttpUtil.post(HOST + CREATE_FEATURE_ENDPOINT, JSONUtil.toJsonStr(createFeatureParam));
        log.info("创建声纹, 返回结果: {}", result);
    }

    /**
     * 创建声纹组
     */
    private static void createGroup() {
        String nonce = IdUtil.simpleUUID();
        long timestamp = System.currentTimeMillis();
        String sign = getSign(nonce, timestamp);
        // 组装 创建声纹组 参数
        Dict creatGroupParam = Dict.create().set("appkey", APP_KEY).set("timestamp", timestamp).set("nonce", nonce)
                .set("sign", sign).set("groupId", GROUP_ID).set("groupInfo", GROUP_INFO);
        String result = HttpUtil.post(HOST + CREATE_GROUP_ENDPOINT, JSONUtil.toJsonStr(creatGroupParam));
        log.info("创建声纹组, 返回结果: {}", result);
    }

    /**
     * 获取签名
     *
     * @param nonce
     * @return
     */
    public static String getSign(String nonce, Long timestamp) {
        StringBuilder sb = new StringBuilder();
        sb.append(APP_KEY).append(timestamp).append(SECRET).append(nonce);
        return getSHA256(sb.toString());
    }

    /**
     * Sha256 摘要加密
     *
     * @param data
     * @return
     */
    public static String getSHA256(String data) {
        String digest = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] bytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
            digest = byte2hex(bytes);
        } catch (Exception e) {
            log.error("No support algorithm!", e);
        }

        return digest;
    }

    /**
     * 二进制转十六进制字符串
     *
     * @param bytes
     * @return
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }
}